﻿using Devil;
using GameToolkit.BehaviourTree;
using System;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.VersionControl;
using UnityEngine;
using UnityEngine.UIElements;

namespace GameToolkit.Editor
{
    internal class RegisterNodeVisual : Node, INodeWithComposite
    {
        public event System.Action<RegisterNodeVisual> OnRenamed;

        BehaviourTreeLib.CompositeInfo mInfo;
        Composite mComposite;
        Port mInput;
        Port mOutput;
        RegisterNodeVisual mRef;

        public bool IsDecoratorEditable => false;

        public int temporaryPortId { get; set; }

        public Vector2 position => GetPosition().position;

        public BehaviourTreeLib.CompositeInfo compositeInfo => mInfo;

        public Composite composite
        {
            get { return mComposite; }
            set
            {
                if (composite != value)
                    throw new System.InvalidOperationException("composite is readonly");
            }
        }

        public RegisterNodeVisual ReferenceTo
        {
            get { return mRef; }
            set
            {
                if (mRef != value && mComposite is RegisterReference)
                {
                    if (mRef != null)
                        mRef.OnRenamed -= OnRefRenamed;
                    mRef = value;
                    if (mRef != null)
                        mRef.OnRenamed += OnRefRenamed;
                }
            }
        }

        public bool GetReferenceTo(BehaviourTreeGraph graph)
        {
            if (mComposite is RegisterReference reg)
            {
                if (reg.Register == null)
                    return false;
                foreach (var node in graph.nodes)
                {
                    if (node is RegisterNodeVisual cnode && cnode.composite == reg.Register)
                    {
                        ReferenceTo = cnode;
                        return true;
                    }
                }
                return false;
            }
            else
            {
                return mComposite is RegisterComposite;
            }
        }

        public RegisterNodeVisual(BehaviourTreeLib.CompositeInfo info, Composite composite) : base()
        {
            mInfo = info;
            mComposite = composite;
            capabilities &= ~Capabilities.Collapsible;

            Init();
        }

        void Init()
        {
            inputContainer.AddToClassList("title-space");
            var p = inputContainer.parent;
            p.Remove(inputContainer);
            titleContainer.Insert(0, inputContainer);

            outputContainer.AddToClassList("title-space");
            p = outputContainer.parent;
            p.Remove(outputContainer);
            titleContainer.Add(outputContainer);

            if (composite is RegisterComposite)
            {
                mInput = InstantiatePort(Orientation.Horizontal, Direction.Input, Port.Capacity.Single, typeof(NodeState));
                mInput.name = "port";
                mInput.portName = "";
                mInput.portColor = LogicNodeVisual.INPUT_COLOR;
                inputContainer.Insert(0, mInput);

                capabilities |= Capabilities.Renamable;

            }
            else
            {

                mOutput = InstantiatePort(Orientation.Horizontal, Direction.Output, Port.Capacity.Multi, typeof(NodeState));
                mOutput.name = "port";
                mOutput.portName = "";
                mOutput.portColor = LogicNodeVisual.INPUT_COLOR;
                outputContainer.Add(mOutput);

            }

            if (composite != null)
                SetPosition(new Rect(composite.m_Position, Vector2.zero));
            //this.Q("contents").visible = false;
            //var btn = this.Q("title-button-container");

            title = BehaviourTreeLib.Instance.GetDescription(composite);
            var style = BehaviourTreeGraphUtils.GetStyleSheet();
            if (style != null)
                styleSheets.Add(style);
        }

        public void SaveCompositeConnections(MinHeap<INodeWithComposite> nodesBuffer)
        {
            if(composite is RegisterComposite reg)
            {
                CompositeState cinput = default;
                foreach (var edge in mInput.connections)
                {
                    var node = edge.output.node as INodeWithComposite;
                    if (node == null || node.composite == null)
                        continue;
                    var id = node.GetOutputPortId(edge);
                    cinput.composite = node.composite;
                    cinput.state = (NodeState)id;
                    break;
                }
                reg.Source = cinput;
            }
        }

        public void LoadCompositeConnections(BehaviourTreeGraph.GraphEdgeLoader edgeLoader)
        {
            if (composite is RegisterComposite reg)
            {
                var node = edgeLoader.GetNode(reg.Source.composite);
                if (node != null)
                {
                    var port = node.GetOutputPort((int)reg.Source.state);
                    edgeLoader.NewEdge<LinkEdge>(mInput, port);
                }
            }
        }

        public void AutoConnectOnCreate(BehaviourTreeGraph graph, Port port) 
        {
            if(port.direction == Direction.Input)
            {
                mOutput.DisconnectAll();
                graph.NewEdge<LinkEdge>(port, mOutput);
            }

        }

        public bool IsCompatiblePort(Port port)
        {
            if (composite is RegisterReference)
                return port.node is TreeGraphNode tnode && tnode.composite is ExecutableComposite;
            else
                return port.node is not RegisterNodeVisual;
        }

        private void OnRefRenamed(RegisterNodeVisual visual)
        {
            title = BehaviourTreeLib.Instance.GetDescription(composite);
        }

        public int GetInputPortId(Edge edge)
        {
            return 0;
        }

        public Port GetInputPort(int portId)
        {
            return mInput;
        }

        public Port GetOutputPort(int portId)
        {
            return mOutput;
        }

        public int GetOutputPortId(Edge edge)
        {
            return (int)NodeState.Success;
        }

        public override Port InstantiatePort(Orientation orientation, Direction direction, Port.Capacity capacity, System.Type type)
        {
            if (type == typeof(NodeState))
                return Port.Create<LinkEdge>(orientation, direction, capacity, type);
            else
                return Port.Create<FlowEdge>(orientation, direction, capacity, type);
        }

        public void Validate()
        {
            var txt = BehaviourTreeLib.Instance.GetDescription(composite);
            if (txt != title)
            {
                title = txt;
                if (mRef != null)
                    mRef.Validate();
                else if (OnRenamed != null)
                    OnRenamed(this);
            }
        }

    }
}